/*
Copyright 2009-2010 Igor Polevoy 

Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
See the License for the specific language governing permissions and 
limitations under the License. 
 */

package org.javalite.activejdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Igor Polevoy
 */
public class ConnectionsAccess {
	private final static Logger logger = LoggerFactory
			.getLogger(ConnectionsAccess.class);
	private static HashMap<String, Object> connectionsorDs = new HashMap<String, Object>();
	private static ThreadLocal<HashMap<String, Connection>> connections = new ThreadLocal<HashMap<String, Connection>>();

	static Map<String, Object> getConnectionOrDs() {
		return connectionsorDs;
	}

	static HashMap<String, Connection> getConnectionMap() {
		if (connections.get() == null)
			connections.set(new HashMap<String, Connection>());
		return connections.get();
	}

	/**
	 * Returns a named connection attached to current thread and bound to name
	 * specified by argument.
	 * 
	 * @param dbName
	 *            name of connection to retrieve.
	 * @return a named connection attached to current thread and bound to name
	 *         specified by argument.
	 */
	static Connection getConnection(String dbName) {

		HashMap<String, Connection> tlMap = getConnectionMap();
		Connection connection = tlMap.get(dbName);

		if (connection == null) {
			Object object = getConnectionOrDs().get(dbName);
			if (object instanceof DataSource) {
				try {
					connection = ((DataSource) object).getConnection();
				} catch (SQLException e) {
					LogFilter.log(logger,
							"get datasourc failur " + e.getMessage());
					return null;
				}
			} else {
				connection = (Connection) object;
			}
			tlMap.put(dbName, connection);
			connections.set(tlMap);
		}

		return connection;

	}

	/**
	 * Attaches a connection to a ThreadLocal and binds it to a name.
	 * 
	 * @param dbName
	 * @param connection
	 */
	static void attach(String dbName, Connection connection) {
		if (ConnectionsAccess.getConnectionOrDs().get(dbName) != null) {
			throw new InternalException(
					"You are opening a connection "
							+ dbName
							+ " without closing a previous one. Check your logic. Connection still remains on thread: "
							+ ConnectionsAccess.getConnectionOrDs().get(dbName));
		}
		LogFilter.log(logger, "Attaching connection: " + connection);
		ConnectionsAccess.getConnectionOrDs().put(dbName, connection);
		LogFilter.log(logger, "Opened connection:" + connection + " named: "
				+ dbName + " on thread: " + Thread.currentThread());
	}

	static void attachDataSource(String dbName, DataSource datasource) {
		if (ConnectionsAccess.getConnectionOrDs().get(dbName) != null) {
			throw new InternalException(
					"You are opening a connection "
							+ dbName
							+ " without closing a previous one. Check your logic. Connection still remains on thread: "
							+ ConnectionsAccess.getConnectionOrDs().get(dbName));
		}
		LogFilter.log(logger, "Attaching datasource: " + datasource);
		ConnectionsAccess.getConnectionOrDs().put(dbName, datasource);
		LogFilter.log(logger, "Opened connection:" + datasource + " named: "
				+ dbName + " on thread: " + Thread.currentThread());
	}

	static void detach(String dbName) {
		LogFilter.log(logger, "Detached connection: " + dbName);
		Object object = getConnectionOrDs().get(dbName);
		if (!(object instanceof DataSource)) {
			getConnectionOrDs().remove(dbName);
		} else {
			getConnectionMap().remove(dbName);
		}
	}
}
